from machine import Pin, I2C, ADC
from time import sleep
import bme280
import framebuf
import utime
import freesans20
import writer
from ssd1306 import SSD1306_I2C

# Initialize I2C for BME280 sensor and OLED display
i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=400000)
baro = 29.92  # Initial barometric pressure in inHg, adjustable by user

i2c2 = I2C(1, sda=Pin(26), scl=Pin(27), freq=400000)
dsp = SSD1306_I2C(128, 64, i2c2)
dsp.fill(0)
dsp.show()

# Define analog input for variable resistor (assume Pin 28 for ADC input)
analog_in = ADC(Pin(28))

# Define switches for coarse and fine adjustments
coarse_adjust = Pin(7, Pin.IN, Pin.PULL_UP)
fine_adjust = Pin(8, Pin.IN, Pin.PULL_UP)
debounce_time = 200  # debounce time in milliseconds
last_coarse_time = utime.ticks_ms()
last_fine_time = utime.ticks_ms()

mode = "altitude"  # Initial mode: 'altitude' or 'pressure_adjust'
last_switch_time = utime.ticks_ms()  # Initialize debounce timer for mode switch
switch = Pin(15, Pin.IN, Pin.PULL_UP)

# Compensation function for pressure adjustment
def temperature_pressure_compensation(pressure, temperature):
    temp_compensation_factor = 1.5
    corrected_pressure = pressure + (temp_compensation_factor * (temperature - 25))
    return corrected_pressure

# Toggle mode function with debounce
def toggle_mode(pin):
    global mode, last_switch_time
    current_time = utime.ticks_ms()
    if utime.ticks_diff(current_time, last_switch_time) > debounce_time:
        mode = "pressure_adjust" if mode == "altitude" else "altitude"
        last_switch_time = current_time

# Attach the interrupt to the mode switch
switch.irq(trigger=Pin.IRQ_FALLING, handler=toggle_mode)

# Pressure adjustment based on analog input and switches
def adjust_pressure():
    global baro
    # Read and scale analog input (assume range maps to 29.50 to 30.50 inHg)
    analog_value = analog_in.read_u16()
    mapped_pressure = 29.5 + (analog_value / 65535) * 1.0  # Scale to a 1.0 inHg range

    # Adjust baro with coarse or fine increments
    if not coarse_adjust.value():
        baro = mapped_pressure if (utime.ticks_diff(utime.ticks_ms(), last_coarse_time) > debounce_time) else baro
    elif not fine_adjust.value():
        baro = round(mapped_pressure, 2) if (utime.ticks_diff(utime.ticks_ms(), last_fine_time) > debounce_time) else baro
        
font_writer = writer.Writer(dsp, freesans20)
while True:
    bme = bme280.BME280(i2c=i2c)
    temp = float(bme.temperature.replace('C', '').strip())
    pres = float(bme.pressure.replace('hPa', '').strip())
    corrected_pressure = temperature_pressure_compensation(pres, temp)

    if mode == "altitude":
        altitude = (baro - (corrected_pressure / 33.8638866667)) * 1000
        alt = int(altitude)
        
        # Display altitude on OLED
        dsp.fill(0)
        font_writer.set_textpos(5, 30)
        font_writer.printstring(f"{alt}")
        font_writer.set_textpos(60, 30)
        font_writer.printstring("FT")
        dsp.show()

    elif mode == "pressure_adjust":
        adjust_pressure()

        # Display current barometric pressure for adjustment
        dsp.fill(0)
        font_writer.set_textpos(5, 20)
        font_writer.printstring("Set Pressure:")
        font_writer.set_textpos(20, 40)
        font_writer.printstring(f"{baro:.2f} inHg")
        dsp.show()

    sleep(1)

1. Imports and Initial Setup
The program starts by importing necessary libraries, including Pin, I2C, ADC (for reading analog input from the variable resistor), and the display-related libraries.
It initializes the I2C connections for both the BME280 sensor (for pressure and temperature readings) and the SSD1306 OLED display.
Constants and initial values for the barometric pressure and debounce timing are defined.

2. Pin Configuration
Analog Input (Variable Resistor): Configured on an ADC pin, this will allow the user to set barometric pressure by adjusting the variable resistor.
Switches:
One switch toggles between two display modes: altitude display and pressure adjustment.
Two other switches control coarse and fine adjustments to the barometric pressure.
These switches are configured as inputs with pull-up resistors to avoid floating input states.

3. Display Mode Handling
The program uses a mode variable to keep track of the current display state:
Altitude Mode: Displays altitude calculated from the corrected pressure.
Pressure Adjustment Mode: Allows the user to view and adjust the barometric pressure.

4. Function Definitions
Temperature and Pressure Compensation:
This function compensates the raw pressure reading from the BME280 sensor based on temperature. Adjusting for temperature provides a more accurate pressure value.
Toggle Mode Function:
This function handles the switching between the altitude display and pressure adjustment modes, using a debouncing technique to prevent unintended rapid toggling. An interrupt on the toggle switch triggers this function.
Adjust Pressure Function:
The adjust_pressure() function reads the analog value from the variable resistor and maps it to a barometric pressure range (e.g., 29.5 to 30.5 inHg).
It then applies either a coarse or fine adjustment based on which switch is pressed, updating the barometric pressure (baro) accordingly.

5. Main Loop
Sensor Reading and Compensation:
Each iteration of the loop begins by reading the temperature and pressure from the BME280 sensor. The raw pressure is compensated for temperature using the temperature_pressure_compensation() function.

Mode-Specific Display:
Altitude Display Mode:
The program calculates altitude from the current barometric pressure (baro) and the compensated pressure reading, converting it to feet.
It displays the calculated altitude on the OLED, refreshing every second.
Pressure Adjustment Mode:
The adjust_pressure() function is called to adjust the baro variable based on the analog input and switches.
The OLED shows the current barometric pressure value, allowing the user to view changes in real-time as they adjust the variable resistor or press the adjustment switches.
Sleep:

The loop ends with a sleep(1) delay to limit the display refresh rate to once per second, easing readability and reducing unnecessary power consumption.

Program Flow Summary
Start in Altitude Mode with baro set to a default value.
Loop Execution:
If in Altitude Mode: Display altitude calculated from barometric pressure and corrected sensor readings.
If in Pressure Adjustment Mode: Allow barometric pressure adjustments, displaying the current value for real-time feedback.
Toggle Mode: Use the mode switch to alternate between altitude and pressure adjustment views.